import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;


/**
 * @author Mehdi HAMOU
 * @author Jeremy LOBBRECHT
 * classe abstraite des graphs qui impl�mente tous les m�thodes g�n�ralis� de lecture des graphs
 */
public abstract class Graph {
	
	/**
	 * indique si le graphe est dirige ou non
	 */
	protected boolean isdirected;
	/**
	 * nom du graphe
	 */
	protected String name;
	/**
	 * chemin vers le fichier dot
	 */
	protected String path;
	/**
	 *nom du fichier dot (sans l'extension)
	 */
	protected String fileName;
	/**
	 *liste d'adjacence
	 */
	protected ArrayList<LinkedList<Cel>> list;
	/**
	 * type du graph (ponderee,flow ou simple)
	 */
	protected String type;

	/**
	 * classe abstraite d'une cellule
	 * @author Mehdi HAMOU
	 * @author Jeremy LOBBRECJ
	 */
	abstract class Cel{
		int vertex;

		public Cel(int vertex){
			this.vertex=vertex;
		}

		public int getVertex(){
			return vertex;
		}
		
		@Override
		public abstract String toString();

	}

	/**
	 * constructeur qui nous permet de lire un fichier et de cr�er les listes d'adjacences de tous les sommets
	 * ce constructeur ne nous permet pas de lire les flows ou les pond�rations du graph c'est l'appel � la methode addCel() impl�ment�e dans les classes filles
	 * @param path chemin d'acces au fichier
	 * @param fileName nom du fichier
	 * @throws IOException
	 * @throws DotException
	 */
	public Graph(String path,String fileName) throws IOException, DotException{
		int predecesseur;
		list=new ArrayList<LinkedList<Cel>>(); 
		if (path.endsWith("/")) this.path = path;
		else this.path = path + "/";
		this.fileName = fileName;
		DotParser tok = new DotParser(this.path + fileName);
		// parse the type of the graph 
		tok.next();
		if (tok.ttype==DotParser.TT_WORD && tok.sval.equalsIgnoreCase("digraph"))
			this.isdirected=true;
		else if (tok.ttype==DotParser.TT_WORD && tok.sval.equalsIgnoreCase("graph"))
			this.isdirected=false;
		else throw new DotException(tok);
		// parse the name of the graph
		tok.next();
		if (tok.ttype == DotParser.TT_WORD)
			this.name =tok.sval;
		else throw new DotException(tok);
		// parse l'accolade ouvrante du corps du graphe
		tok.next();
		if (tok.ttype == DotParser.TT_LBRACE);
		else throw new DotException(tok);
		tok.next();
		while(tok.ttype!=DotParser.TT_RBRACE ){
			if(tok.ttype==DotParser.TT_NUMBER) {
				predecesseur=(int)tok.nval;
				tok.next();
			}
			else throw new DotException(tok,"Donnez nous un entier !");
			if(tok.ttype==DotParser.TT_SEM) {
				list.add(predecesseur,new LinkedList<Cel>());
				tok.next();
			}
			if((isdirected&&tok.ttype==DotParser.TT_ARROW) || (!isdirected&&tok.ttype==DotParser.TT_EDGE)){
				//ancienne valeur , nouvelle valeur
				tok.next();
				if(tok.ttype==DotParser.TT_NUMBER) {
					addCel(tok,(int)predecesseur);
					tok.next();
					if(tok.ttype==DotParser.TT_SEM) {
						tok.next();
					}
					else throw new DotException(tok,"Il faut finir par un \";\"");
				}
				else throw new DotException(tok,"Donnez nous un entier !");
			}
		}
		tok.next();
		if(tok.ttype!=DotParser.TT_EOF) throw new DotException(tok,"mauvaise fin de fichier !");
	}	
	
	/**
	 * constructeur qui cree un graphe vide
	 */
	public Graph(int nbVertex){
		path="/home/lobbrecj/workspace/RO/InOutput/";
		fileName="newGraph.dot";
		list=new ArrayList<LinkedList<Cel>>();
		for(int i=0;i<nbVertex;i++)
			list.add(i,new LinkedList<Cel>());
			
	}
	
	/**
	 * cette m�thode nous permet d'ajouter une adjacence en plus dans la liste actuelle, elle nous permet aussi de lire s'il y a un flow ou une pond�ration pour l'ajouter aussi au niveau de la cellule
	 * @param tok
	 * @param predecesseur
	 * @throws IOException
	 * @throws DotException
	 */
	public abstract void addCel(DotParser tok,int predecesseur) throws IOException, DotException;
	
	/**
	 * @return retourne une chaine de caractere representant le graph
	 */
	public String toString(){
		int i=0;
		String s="";
		if(this.isdirected) {
			s+="di";
		}
		s+="graph "+this.type+"{\n";
		Iterator <LinkedList<Cel>> it=list.iterator();
		Iterator <Cel> it1;
		LinkedList<Cel> e;
		while(it.hasNext()) {
			e=it.next();
			it1=e.iterator();
			s+=i+";\n";
			while(it1.hasNext()) {
				s+=i;
				if(this.isdirected) {
					s+=" -> ";
				}
				else {
					s+=" -- ";
				}
				s+=it1.next().toString()+";\n";
			}
			i++;
		}
		s+=" }";
		return s;
	}


	/**
	 * fonction de cr�ation du fichier
	 * @return 
	 */
	public boolean createDot(){
		File file=new File(this.path + this.fileName );
		try {
			file.createNewFile();
			FileWriter fw = null;
			// écriture du graphe dans le fichier
			fw = new FileWriter(file);
			fw.write(this.toString());
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	 * fonction qui retourne le nombre de sommet dans un graph
	 * @return retourne le nombre de vertex
	 */
	public int nbVertex(){
		Iterator <LinkedList<Cel>> it=list.iterator();
		int cpt=0;
		while(it.hasNext()){
			cpt++;
			it.next();
		}
		return cpt;
	}

}
